﻿ /**
* @class Bar2DChart
* @author InfoSoft Global (P) Ltd. www.InfoSoftGlobal.com
* @version 3.0
*
* Copyright (C) InfoSoft Global Pvt. Ltd. 2005-2006
*
* Bar2DChart chart extends the SingleYAxis2DHorizontalChart class to render a
* 2D Bar Chart.
*/
//Import Chart class
import com.fusioncharts.core.Chart;
//Parent SingleYAxis2DHorizontalChart Class
import com.fusioncharts.core.SingleYAxis2DHorizontalChart;
//Error class
import com.fusioncharts.helper.FCError;
//Import Logger Class
import com.fusioncharts.helper.Logger;
//Style Object
import com.fusioncharts.core.StyleObject;
//Delegate
import mx.utils.Delegate;
//Bar2D Class
import com.fusioncharts.core.chartobjects.Bar2D;
//Extensions
import com.fusioncharts.extensions.ColorExt;
import com.fusioncharts.extensions.StringExt;
import com.fusioncharts.extensions.MathExt;
import com.fusioncharts.extensions.DrawingExt;
class com.fusioncharts.core.charts.Bar2DChart extends SingleYAxis2DHorizontalChart 
{
	//Version number (if different from super Chart class)
	//private var _version:String = "3.0.0";
	//Instance variables
	//List of chart objects
	private var _arrObjects : Array;
	private var xmlData : XML;
	//Array to store data objects
	private var data : Array;
	//Number of data items
	private var num : Number;
	/**
	* Constructor function. We invoke the super class'
	* constructor and then set the objects for this chart.
	*/
	function Bar2DChart (targetMC : MovieClip, depth : Number, width : Number, height : Number, x : Number, y : Number, debugMode : Boolean, lang : String)
	{
		//Invoke the super class constructor
		super (targetMC, depth, width, height, x, y, debugMode, lang);
		//Log additional information to debugger
		//We log version from this class, so that if this class version
		//is different, we can log it
		this.log ("Version", _version, Logger.LEVEL.INFO);
		this.log ("Chart Type", "Single Series 2D Bar Chart", Logger.LEVEL.INFO);
		//List Chart Objects and set them
		_arrObjects = new Array ("BACKGROUND", "CANVAS", "CAPTION", "SUBCAPTION", "YAXISNAME", "XAXISNAME", "DIVLINES", "YAXISVALUES", "VGRID", "DATALABELS", "DATAVALUES", "TRENDLINES", "TRENDVALUES", "DATAPLOT", "TOOLTIP", "VLINES");
		super.setChartObjects (_arrObjects);
		//Initialize the data structure (array) for single series chart
		this.data = new Array ();
		//Initialize the number of data elements present
		this.num = 0;
		//Whether negative number present
		this.config.negativePresent = false;
	}
	/**
	* render method is the single call method that does the rendering of chart:
	* - Parsing XML
	* - Calculating values and co-ordinates
	* - Visual layout and rendering
	* - Event handling
	*/
	public function render () : Void 
	{
		//Parse the XML Data document
		this.parseXML ();
		//Now, if the number of data elements is 0, we show pertinent
		//error.
		if (this.num == 0)
		{
			tfAppMsg = this.renderAppMessage (_global.getAppMessage ("NODATA", this.lang));
			//Add a message to log.
			this.log ("No Data to Display", "No data was found in the XML data document provided. Possible cases can be: <LI>There isn't any data generated by your system. If your system generates data based on parameters passed to it using dataURL, please make sure dataURL is URL Encoded.</LI><LI>You might be using a Single Series Chart .swf file instead of Multi-series .swf file and providing multi-series data or vice-versa.</LI>", Logger.LEVEL.ERROR);
		} else 
		{
			//Detect number scales
			this.detectNumberScales ();
			//Calculate the axis limits
			this.calculateAxisLimits ();
			//Calculate exact number of div lines
			this.calcDivs ();
			//Set Style defaults
			this.setStyleDefaults ();
			//Validate trend lines
			this.validateTrendLines ();
			//Calculate Points
			this.calculatePoints ();
			//Calculate vLine Positions
			this.calcVLinesPos ();
			//Calculate trend line positions
			this.calcTrendLinePos ();
			//Allot the depths for various charts objects now
			this.allotDepths ();
			//Feed macro values
			this.feedMacros ();
			//Remove application message
			this.removeAppMessage (this.tfAppMsg);
			//Set tool tip parameter
			this.setToolTipParam ();
			//-----Start Visual Rendering Now------//
			//Draw background
			this.drawBackground ();
			//Set click handler
			this.drawClickURLHandler ()
			//Load background SWF
			this.loadBgSWF ();
			//Update timer
			this.timeElapsed = (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.BACKGROUND) : 0;
			//Draw canvas
			this.config.intervals.canvas = setInterval (Delegate.create (this, drawCanvas) , this.timeElapsed);
			//Draw headers
			this.config.intervals.headers = setInterval (Delegate.create (this, drawHeaders) , this.timeElapsed);
			//Update timer
			this.timeElapsed += (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.CANVAS, this.objects.CAPTION, this.objects.SUBCAPTION, this.objects.YAXISNAME, this.objects.XAXISNAME) : 0;
			//Draw div lines
			this.config.intervals.divLines = setInterval (Delegate.create (this, drawDivLines) , this.timeElapsed);
			//Update timer
			this.timeElapsed += (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.DIVLINES, this.objects.YAXISVALUES) : 0;
			//Horizontal grid
			this.config.intervals.vGrid = setInterval (Delegate.create (this, drawVGrid) , this.timeElapsed);
			//Update timer
			this.timeElapsed += (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.HGRID) : 0;
			//Draw labels
			this.config.intervals.labels = setInterval (Delegate.create (this, drawLabels) , this.timeElapsed);
			//Draw columns
			this.config.intervals.plot = setInterval (Delegate.create (this, drawBars) , this.timeElapsed);
			//Update timer
			this.timeElapsed += (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.DATALABELS, this.objects.DATAPLOT) : 0;
			//Data Values
			this.config.intervals.dataValues = setInterval (Delegate.create (this, drawValues) , this.timeElapsed);
			//Draw trend lines
			this.config.intervals.trend = setInterval (Delegate.create (this, drawTrendLines) , this.timeElapsed);
			//Draw vertical div lines
			this.config.intervals.vLine = setInterval (Delegate.create (this, drawVLines) , this.timeElapsed);
			//Update timer
			this.timeElapsed += (this.params.animation) ? this.styleM.getMaxAnimationTime (this.objects.TRENDLINES, this.objects.TRENDVALUES, this.objects.VLINES, this.objects.DATAVALUES) : 0;
			//Render context menu
			//We do not put context menu interval as we need the menu to appear
			//right from start of the play.
			this.setContextMenu ();
		}
	}
	/**
	* returnDataAsObject method creates an object out of the parameters
	* passed to this method. The idea is that we store each data point
	* as an object with multiple (flexible) properties. So, we do not
	* use a predefined class structure. Instead we use a generic object.
	*	@param	label		Label of the data column.
	*	@param	value		Value for the column.
	*	@param	color		Hex Color code (or comma separated list).
	*	@param	alpha		List of alphas separated by comma
	*	@param	ratio		List of color ratios separated by comma
	*	@param	toolText	Tool tip text (if specified).
	*	@param	link		Link (if any) for the column.
	*	@param	showLabel	Flag to show/hide label for this column.
	*	@param	showValue	Flag to show/hide value for this column.
	*	@param	isDashed	Flag whether the column would have dashed border.
	*	@return			An object encapsulating all these properies.
	*/
	private function returnDataAsObject (label : String, value : Number, color : String, alpha : String, ratio : String, toolText : String, link : String, showLabel : Number, showValue : Number, isDashed : Boolean) : Object 
	{
		//Create a container
		var dataObj : Object = new Object ();
		//Store the values
		dataObj.label = label;
		dataObj.value = value;
		//Extract and save colors, ratio, alpha as array so that we do not have to parse later.
		dataObj.color = ColorExt.parseColorList (color);
		dataObj.alpha = ColorExt.parseAlphaList (alpha, dataObj.color.length);
		dataObj.ratio = ColorExt.parseRatioList (ratio, dataObj.color.length);
		dataObj.toolText = toolText;
		dataObj.link = link;
		dataObj.showLabel = (showLabel == 1) ? true : false;
		dataObj.showValue = (showValue == 1) ? true : false;
		dataObj.dashed = isDashed;
		//If the given number is not a valid number or it's missing
		//set appropriate flags for this data point
		dataObj.isDefined = ((dataObj.alpha [0] == 0) || isNaN (value)) ? false : true;
		//Other parameters
		//X & Y Position of data point
		dataObj.x = 0;
		dataObj.y = 0;
		//Width and height
		dataObj.w = 0;
		dataObj.h = 0;
		//X & Y Position of value tb
		dataObj.valTBX = 0;
		dataObj.valTBY = 0;
		//Return the container
		return dataObj;
	}
	/**
	* parseXML method parses the XML data, sets defaults and validates
	* the attributes before storing them to data storage objects.
	*/
	private function parseXML () : Void 
	{
		//Get the element nodes
		var arrDocElement : Array = this.xmlData.childNodes;
		//Loop variable
		var i : Number;
		var j : Number;
		//Look for <graph> element
		for (i = 0; i < arrDocElement.length; i ++)
		{
			//If it's a <graph> element, proceed.
			//Do case in-sensitive mathcing by changing to upper case
			if (arrDocElement [i].nodeName.toUpperCase () == "GRAPH" || arrDocElement [i].nodeName.toUpperCase () == "CHART")
			{
				//Extract attributes of <graph> element
				this.parseAttributes (arrDocElement [i]);
				//Now, get the child nodes - first level nodes
				//Level 1 nodes can be - CATEGORIES, DATASET, TRENDLINES, STYLES etc.
				var arrLevel1Nodes : Array = arrDocElement [i].childNodes;
				var setNode : XMLNode;
				//Iterate through all level 1 nodes.
				for (j = 0; j < arrLevel1Nodes.length; j ++)
				{
					//If it's Data nodes
					if (arrLevel1Nodes [j].nodeName.toUpperCase () == "SET")
					{
						//Set Node. So extract the data.
						//First, updated counter
						this.num ++;
						//Get reference to node.
						setNode = arrLevel1Nodes [j];
						//Get attributes
						var atts : Array;
						atts = this.getAttributesArray (setNode);
						//Extract values.
						var setName : String = getFV (atts ["label"] , atts ["name"] , "");
						//Now, get value.
						var setValue : Number = this.getSetValue (atts ["value"]);
						//We do NOT unescape the link, as this will be done
						//in invokeLink method for the links that user clicks.
						var setLink : String = getFV (atts ["link"] , "");
						var setToolText : String = getFV (atts ["tooltext"] , atts ["hovertext"]);
						var setColor : String = getFV (atts ["color"] , this.defColors.getColor ());
						//If plot gradient color has been defined, add it
						if (this.params.plotGradientColor != "")
						{
							setColor = setColor + "," + this.params.plotGradientColor;
						}
						var setAlpha : String = getFV (atts ["alpha"] , this.params.plotFillAlpha);
						var setRatio : String = getFV (atts ["ratio"] , this.params.plotFillRatio);
						var setShowLabel : Number = getFN (atts ["showlabel"] , atts ["showname"] , this.params.showLabels);
						var setShowValue : Number = getFN (atts ["showvalue"] , this.params.showValues);
						var setDashed : Boolean = toBoolean (getFN (atts ["dashed"] , this.params.plotBorderDashed));
						//Store all these attributes as object.
						this.data [this.num] = this.returnDataAsObject (setName, setValue, setColor, setAlpha, setRatio, setToolText, setLink, setShowLabel, setShowValue, setDashed);
						//If number is negative, set appropriate flags
						//this.config.negativePresent = (setValue<0) ? true : false;
						
					} else if (arrLevel1Nodes [j].nodeName.toUpperCase () == "VLINE")
					{
						//Vertical axis division Node - extract child nodes
						var vLineNode : XMLNode = arrLevel1Nodes [j];
						//Parse and store
						this.parseVLineNode (vLineNode, this.num);
					} 
					else if (arrLevel1Nodes [j].nodeName.toUpperCase () == "STYLES")
					{
						//Styles Node - extract child nodes
						var arrStyleNodes : Array = arrLevel1Nodes [j].childNodes;
						//Parse the style nodes to extract style information
						super.parseStyleXML (arrStyleNodes);
					} else if (arrLevel1Nodes [j].nodeName.toUpperCase () == "TRENDLINES")
					{
						//Trend lines node
						var arrTrendNodes : Array = arrLevel1Nodes [j].childNodes;
						//Parse the trend line nodes
						super.parseTrendLineXML (arrTrendNodes);
					}
				}
			}
		}
		//Delete all temporary objects used for parsing XML Data document
		delete setNode;
		delete arrDocElement;
		delete arrLevel1Nodes;
	}
	/**
	* parseAttributes method parses the attributes and stores them in
	* chart storage objects.
	* Starting ActionScript 2, the parsing of XML attributes have also
	* become case-sensitive. However, prior versions of FusionCharts
	* supported case-insensitive attributes. So we need to parse all
	* attributes as case-insensitive to maintain backward compatibility.
	* To do so, we first extract all attributes from XML, convert it into
	* lower case and then store it in an array. Later, we extract value from
	* this array.
	* @param	graphElement	XML Node containing the <graph> element
	*							and it's attributes
	*/
	private function parseAttributes (graphElement : XMLNode) : Void 
	{
		//Array to store the attributes
		var atts : Array = this.getAttributesArray (graphElement);
		//NOW IT'S VERY NECCESARY THAT WHEN WE REFERENCE THIS ARRAY
		//TO GET AN ATTRIBUTE VALUE, WE SHOULD PROVIDE THE ATTRIBUTE
		//NAME IN LOWER CASE. ELSE, UNDEFINED VALUE WOULD SHOW UP.
		//Extract attributes pertinent to this chart
		//Which palette to use?
		this.params.palette = getFN (atts ["palette"] , 1)
		//Whether to show about FusionCharts Menu Item - by default set to on
		this.params.showFCMenuItem = toBoolean(getFN (atts ["showfcmenuitem"] , 1));
		// ---------- PADDING AND SPACING RELATED ATTRIBUTES ----------- //
		//captionPadding = Space between caption/subcaption and canvas start Y
		this.params.captionPadding = getFN (atts ["captionpadding"] , 10);
		//Padding for x-axis name - from top
		this.params.xAxisNamePadding = getFN (atts ["xaxisnamepadding"] , 5);
		//Padding for y-axis name - to right
		this.params.yAxisNamePadding = getFN (atts ["yaxisnamepadding"] , 5);
		//Y-Axis Values padding - Vertical space between the axis edge and
		//y-axis values.
		this.params.yAxisValuesPadding = getFN (atts ["yaxisvaluespadding"] , 2);
		//Label padding - Horizontal space between the labels and canvas start position
		this.params.labelPadding = getFN (atts ["labelpadding"] , atts ["labelspadding"] , 3);
		//Value padding - Horizontal space between the end of bars and start of value textboxes
		this.params.valuePadding = getFN (atts ["valuepadding"] , 2);
		//Percentage space on the plot area
		this.params.plotSpacePercent = getFN (atts ["plotspacepercent"] , 20);
		///Cannot be less than 0 and more than 80
		if ((this.params.plotSpacePercent < 0) || (this.params.plotSpacePercent > 80))
		{
			//Reset to 20
			this.params.plotSpacePercent = 20;
		}
		//Chart Margins - Empty space at the 4 sides
		this.params.chartLeftMargin = getFN (atts ["chartleftmargin"] , 15);
		this.params.chartRightMargin = getFN (atts ["chartrightmargin"] , 15);
		this.params.chartTopMargin = getFN (atts ["charttopmargin"] , 15);
		this.params.chartBottomMargin = getFN (atts ["chartbottommargin"] , 15);
		// -------------------------- HEADERS ------------------------- //
		//Chart Caption and sub Caption
		this.params.caption = getFV (atts ["caption"] , "");
		this.params.subCaption = getFV (atts ["subcaption"] , "");
		//X and Y Axis Name
		this.params.xAxisName = getFV (atts ["xaxisname"] , "");
		this.params.yAxisName = getFV (atts ["yaxisname"] , "");
		//Adaptive yMin - if set to true, the y min will be based on the values
		//provided. It won't be set to 0 in case of all positive values
		this.params.setAdaptiveYMin = toBoolean (getFN (atts ["setadaptiveymin"] , 0));
		// --------------------- CONFIGURATION ------------------------- //
		//The upper and lower limits of y and x axis
		this.params.yAxisMinValue = atts ["yaxisminvalue"];
		this.params.yAxisMaxValue = atts ["yaxismaxvalue"];
		//Whether to set animation for entire chart.
		this.params.animation = toBoolean (getFN (atts ["animation"] , 1));
		//Whether to set the default chart animation
		this.params.defaultAnimation = toBoolean (getFN (atts ["defaultanimation"] , 1));
		//Configuration to set whether to show the labels
		this.params.showLabels = toBoolean (getFN (atts ["showlabels"] , atts ["shownames"] , 1));
		//Step value for labels - i.e., show all labels or skip every x label
		this.params.labelStep = int (getFN (atts ["labelstep"] , 1));
		//Cannot be less than 1
		this.params.labelStep = (this.params.labelStep < 1) ? 1 : this.params.labelStep;
		//Configuration whether to show data values
		this.params.showValues = toBoolean (getFN (atts ["showvalues"] , 1));
		//Whether to place values inside column
		this.params.placeValuesInside = toBoolean (getFN (atts ["placevaluesinside"] , 0));
		//Option to show/hide y-axis values
		this.params.showYAxisValues = getFN (atts ["showyaxisvalues"] , atts ["showyaxisvalue"] , 1);
		this.params.showLimits = toBoolean (getFN (atts ["showlimits"] , this.params.showYAxisValues));
		this.params.showDivLineValues = toBoolean (getFN (atts ["showdivlinevalue"] , atts ["showdivlinevalues"] , this.params.showYAxisValues));
		//Y-axis value step- i.e., show all y-axis or skip every x(th) value
		this.params.yAxisValuesStep = int (getFN (atts ["yaxisvaluesstep"] , atts ["yaxisvaluestep"] , 1));
		//Cannot be less than 1
		this.params.yAxisValuesStep = (this.params.yAxisValuesStep < 1) ? 1 : this.params.yAxisValuesStep;
		//Show bar shadows
		this.params.showBarShadow = toBoolean (getFN (atts ["showshadow"] , atts ["showbarshadow"] , this.defColors.get2DShadow (this.params.palette)));
		//Whether to automatically adjust div lines
		this.params.adjustDiv = toBoolean (getFN (atts ["adjustdiv"] , 1));
		//Whether to rotate x-axis name
		this.params.rotateXAxisName = toBoolean (getFN (atts ["rotatexaxisname"] , 1));
		//Max width to be alloted to x-axis name - No defaults, as it's calculated later.
		this.params.xAxisNameWidth = atts ["xaxisnamewidth"];
		//Click URL
		this.params.clickURL = getFV (atts ["clickurl"] , "");
		// ------------------------- COSMETICS -----------------------------//
		//Background properties - Gradient
		this.params.bgColor = getFV (atts ["bgcolor"] , this.defColors.get2DBgColor (this.params.palette));
		this.params.bgAlpha = getFV (atts ["bgalpha"] , this.defColors.get2DBgAlpha (this.params.palette));
		this.params.bgRatio = getFV (atts ["bgratio"] , this.defColors.get2DBgRatio (this.params.palette));
		this.params.bgAngle = getFV (atts ["bgangle"] , this.defColors.get2DBgAngle (this.params.palette));
		//Border Properties of chart
		this.params.showBorder = toBoolean (getFN (atts ["showborder"] , 1));
		this.params.borderColor = formatColor (getFV (atts ["bordercolor"] , this.defColors.get2DBorderColor (this.params.palette)));
		this.params.borderThickness = getFN (atts ["borderthickness"] , 1);
		this.params.borderAlpha = getFN (atts ["borderalpha"] , this.defColors.get2DBorderAlpha (this.params.palette));
		//Background swf
		this.params.bgSWF = getFV (atts ["bgswf"] , "");
		this.params.bgSWFAlpha = getFN (atts ["bgswfalpha"] , 100);
		//Canvas background properties - Gradient
		this.params.canvasBgColor = getFV (atts ["canvasbgcolor"] , this.defColors.get2DCanvasBgColor (this.params.palette));
		this.params.canvasBgAlpha = getFV (atts ["canvasbgalpha"] , this.defColors.get2DCanvasBgAlpha (this.params.palette));
		this.params.canvasBgRatio = getFV (atts ["canvasbgratio"] , this.defColors.get2DCanvasBgRatio (this.params.palette));
		this.params.canvasBgAngle = getFV (atts ["canvasbgangle"] , this.defColors.get2DCanvasBgAngle (this.params.palette));
		//Canvas Border properties
		this.params.canvasBorderColor = formatColor (getFV (atts ["canvasbordercolor"] , this.defColors.get2DCanvasBorderColor (this.params.palette)));
		this.params.canvasBorderThickness = getFN (atts ["canvasborderthickness"] , 2);
		this.params.canvasBorderAlpha = getFN (atts ["canvasborderalpha"] , this.defColors.get2DCanvasBorderAlpha (this.params.palette));
		//Plot is dashed
		this.params.plotBorderDashed = toBoolean (getFN (atts ["plotborderdashed"] , 0));
		//Dash Properties
		this.params.plotBorderDashLen = getFN (atts ["plotborderdashlen"] , 5);
		this.params.plotBorderDashGap = getFN (atts ["plotborderdashgap"] , 4);
		//Plot cosmetic properties
		this.params.showPlotBorder = toBoolean (getFN (atts ["showplotborder"] , 1));
		this.params.plotBorderColor = formatColor (getFV (atts ["plotbordercolor"] , this.defColors.get2DPlotBorderColor (this.params.palette)));
		this.params.plotBorderThickness = getFN (atts ["plotborderthickness"] , 1);
		this.params.plotBorderAlpha = getFN (atts ["plotborderalpha"] , (this.params.showPlotBorder == true) ?95 : 0);
		//Fill properties
		this.params.plotFillAngle = getFN (atts ["plotfillangle"] , 180);
		this.params.plotFillRatio = getFV (atts ["plotfillratio"] , "");
		this.params.plotFillAlpha = getFV (atts ["plotfillalpha"] , "100");
		//Plot gradient color
		if (atts ["plotgradientcolor"] == "")
		{
			//If some one doesn't want to specify a plot gradient color
			//i.e., he opts for solid fills
			this.params.plotGradientColor = ""
		}else
		{
			this.params.plotGradientColor = formatColor (getFV (atts ["plotgradientcolor"] , this.defColors.get2DPlotGradientColor (this.params.palette)));
		}
		//Vertical grid division Lines - Number, color, thickness & alpha
		//Necessarily need a default value for numDivLines.
		this.params.numDivLines = getFN (atts ["numdivlines"] , 4);
		this.params.divLineColor = formatColor (getFV (atts ["divlinecolor"] , this.defColors.get2DDivLineColor (this.params.palette)));
		this.params.divLineThickness = getFN (atts ["divlinethickness"] , 1);
		this.params.divLineAlpha = getFN (atts ["divlinealpha"] , this.defColors.get2DDivLineAlpha (this.params.palette));
		this.params.divLineIsDashed = toBoolean (getFN (atts ["divlineisdashed"] , 0));
		this.params.divLineDashLen = getFN (atts ["divlinedashlen"] , 4);
		this.params.divLineDashGap = getFN (atts ["divlinedashgap"] , 2);
		//Zero Plane properties
		this.params.showZeroPlane = true;
		this.params.zeroPlaneColor = formatColor (getFV (atts ["zeroplanecolor"] , this.params.divLineColor));
		this.params.zeroPlaneThickness = getFN (atts ["zeroplanethickness"] , (this.params.divLineThickness == 1) ?2 : this.params.divLineThickness);
		this.params.zeroPlaneAlpha = getFN (atts ["zeroplanealpha"] , this.params.divLineAlpha * 2);
		//Alternating grid colors
		this.params.showAlternateVGridColor = toBoolean (getFN (atts ["showalternatevgridcolor"] , 1));
		this.params.alternateVGridColor = formatColor (getFV (atts ["alternatevgridcolor"] , this.defColors.get2DAltVGridColor (this.params.palette)));
		this.params.alternateVGridAlpha = getFN (atts ["alternatevgridalpha"] , this.defColors.get2DAltVGridAlpha (this.params.palette));
		//Tool Tip - Show/Hide, Background Color, Border Color, Separator Character
		this.params.showToolTip = toBoolean (getFN (atts ["showtooltip"] , atts ["showhovercap"] , 1));
		this.params.toolTipBgColor = formatColor (getFV (atts ["tooltipbgcolor"] , atts ["hovercapbgcolor"] , atts ["hovercapbg"] , this.defColors.get2DToolTipBgColor (this.params.palette)));
		this.params.toolTipBorderColor = formatColor (getFV (atts ["tooltipbordercolor"] , atts ["hovercapbordercolor"] , atts ["hovercapborder"] , this.defColors.get2DToolTipBorderColor (this.params.palette)));
		this.params.toolTipSepChar = getFV (atts ["tooltipsepchar"] , atts ["hovercapsepchar"] , ", ");
		//Font Properties
		this.params.baseFont = getFV (atts ["basefont"] , "Verdana");
		this.params.baseFontSize = getFN (atts ["basefontsize"] , 10);
		this.params.baseFontColor = formatColor (getFV (atts ["basefontcolor"] , this.defColors.get2DBaseFontColor (this.params.palette)));
		this.params.outCnvBaseFont = getFV (atts ["outcnvbasefont"] , this.params.baseFont);
		this.params.outCnvBaseFontSize = getFN (atts ["outcnvbasefontsize"] , this.params.baseFontSize);
		this.params.outCnvBaseFontColor = formatColor (getFV (atts ["outcnvbasefontcolor"] , this.params.baseFontColor));
		// ------------------------- NUMBER FORMATTING ---------------------------- //
		//Option whether the format the number (using Commas)
		this.params.formatNumber = toBoolean (getFN (atts ["formatnumber"] , 1));
		//Option to format number scale
		this.params.formatNumberScale = toBoolean (getFN (atts ["formatnumberscale"] , 1));
		//Number Scales
		this.params.defaultNumberScale = getFV (atts ["defaultnumberscale"] , "");
		this.params.numberScaleUnit = getFV (atts ["numberscaleunit"] , "K,M");
		this.params.numberScaleValue = getFV (atts ["numberscalevalue"] , "1000,1000");
		//Number prefix and suffix
		//TODO: Escape them
		this.params.numberPrefix = getFV (atts ["numberprefix"] , "");
		this.params.numberSuffix = getFV (atts ["numbersuffix"] , "");
		//Decimal Separator Character
		this.params.decimalSeparator = getFV (atts ["decimalseparator"] , ".");
		//Thousand Separator Character
		this.params.thousandSeparator = getFV (atts ["thousandseparator"] , ",");
		//Input decimal separator and thousand separator. In some european countries,
		//commas are used as decimal separators and dots as thousand separators. In XML,
		//if the user specifies such values, it will give a error while converting to
		//number. So, we accept the input decimal and thousand separator from user, so that
		//we can covert it accordingly into the required format.
		this.params.inDecimalSeparator = getFV (atts ["indecimalseparator"] , "");
		this.params.inThousandSeparator = getFV (atts ["inthousandseparator"] , "");
		//Decimal Precision (number of decimal places to be rounded to)
		this.params.decimals = getFV (atts ["decimals"] , atts ["decimalprecision"]);
		//Force Decimal Padding
		this.params.forceDecimals = toBoolean (getFN (atts ["forcedecimals"] , 0));
		//y-Axis values decimals
		this.params.yAxisValueDecimals = getFV (atts ["yaxisvaluedecimals"] , atts ["yaxisvaluesdecimals"] , atts ["divlinedecimalprecision"] , atts ["limitsdecimalprecision"]);
	}
	/**
	* getMaxDataValue method gets the maximum y-axis data value present
	* in the data.
	*	@return	The maximum value present in the data provided.
	*/
	private function getMaxDataValue () : Number 
	{
		var maxValue : Number;
		var firstNumberFound : Boolean = false;
		var i : Number;
		for (i = 1; i <= this.num; i ++)
		{
			//By default assume the first non-null number to be maximum
			if (firstNumberFound == false)
			{
				if (this.data [i].isDefined == true)
				{
					//Set the flag that "We've found first non-null number".
					firstNumberFound = true;
					//Also assume this value to be maximum.
					maxValue = this.data [i].value;
				}
			} else 
			{
				//If the first number has been found and the current data is defined, compare
				if (this.data [i].isDefined)
				{
					//Store the greater number
					maxValue = (this.data [i].value > maxValue) ? this.data [i].value : maxValue;
				}
			}
		}
		return maxValue;
	}
	/**
	* getMinDataValue method gets the minimum y-axis data value present
	* in the data
	*	@reurns		The minimum value present in data
	*/
	private function getMinDataValue () : Number 
	{
		var minValue : Number;
		var firstNumberFound : Boolean = false;
		var i : Number;
		for (i = 1; i <= this.num; i ++)
		{
			//By default assume the first non-null number to be minimum
			if (firstNumberFound == false)
			{
				if (this.data [i].isDefined == true)
				{
					//Set the flag that "We've found first non-null number".
					firstNumberFound = true;
					//Also assume this value to be minimum.
					minValue = this.data [i].value;
				}
			} else 
			{
				//If the first number has been found and the current data is defined, compare
				if (this.data [i].isDefined)
				{
					//Store the lesser number
					minValue = (this.data [i].value < minValue) ? this.data [i].value : minValue;
				}
			}
		}
		return minValue;
	}
	/**
	* calculateAxisLimits method sets the axis limits for the chart.
	* It gets the minimum and maximum value specified in data and
	* based on that it calls super.getAxisLimits();
	*/
	private function calculateAxisLimits () : Void 
	{
		this.getAxisLimits (this.getMaxDataValue () , this.getMinDataValue () , true, ! this.params.setAdaptiveYMin);
	}
	/**
	* setStyleDefaults method sets the default values for styles or
	* extracts information from the attributes and stores them into
	* style objects.
	*/
	private function setStyleDefaults () : Void 
	{
		//Default font object for Caption
		//-----------------------------------------------------------------//
		var captionFont = new StyleObject ();
		captionFont.name = "_SdCaptionFont";
		captionFont.align = "center";
		captionFont.valign = "top";
		captionFont.bold = "1";
		captionFont.font = this.params.outCnvBaseFont;
		captionFont.size = this.params.outCnvBaseFontSize;
		captionFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.CAPTION, captionFont, this.styleM.TYPE.FONT, null);
		delete captionFont;
		//-----------------------------------------------------------------//
		//Default font object for SubCaption
		//-----------------------------------------------------------------//
		var subCaptionFont = new StyleObject ();
		subCaptionFont.name = "_SdSubCaptionFont";
		subCaptionFont.align = "center";
		subCaptionFont.valign = "top";
		subCaptionFont.bold = "1";
		subCaptionFont.font = this.params.outCnvBaseFont;
		subCaptionFont.size = this.params.outCnvBaseFontSize;
		subCaptionFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.SUBCAPTION, subCaptionFont, this.styleM.TYPE.FONT, null);
		delete subCaptionFont;
		//-----------------------------------------------------------------//
		//Default font object for YAxisName
		//-----------------------------------------------------------------//
		var yAxisNameFont = new StyleObject ();
		yAxisNameFont.name = "_SdYAxisNameFont";
		yAxisNameFont.align = "center";
		yAxisNameFont.valign = "middle";
		yAxisNameFont.bold = "1";
		yAxisNameFont.font = this.params.outCnvBaseFont;
		yAxisNameFont.size = this.params.outCnvBaseFontSize;
		yAxisNameFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.YAXISNAME, yAxisNameFont, this.styleM.TYPE.FONT, null);
		delete yAxisNameFont;
		//-----------------------------------------------------------------//
		//Default font object for XAxisName
		//-----------------------------------------------------------------//
		var xAxisNameFont = new StyleObject ();
		xAxisNameFont.name = "_SdXAxisNameFont";
		xAxisNameFont.align = "center";
		xAxisNameFont.valign = "middle";
		xAxisNameFont.bold = "1";
		xAxisNameFont.font = this.params.outCnvBaseFont;
		xAxisNameFont.size = this.params.outCnvBaseFontSize;
		xAxisNameFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.XAXISNAME, xAxisNameFont, this.styleM.TYPE.FONT, null);
		delete xAxisNameFont;
		//-----------------------------------------------------------------//
		//Default font object for trend lines
		//-----------------------------------------------------------------//
		var trendFont = new StyleObject ();
		trendFont.name = "_SdTrendFontFont";
		trendFont.font = this.params.outCnvBaseFont;
		trendFont.size = this.params.outCnvBaseFontSize;
		trendFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.TRENDVALUES, trendFont, this.styleM.TYPE.FONT, null);
		delete trendFont;
		//-----------------------------------------------------------------//
		//Default font object for yAxisValues
		//-----------------------------------------------------------------//
		var yAxisValuesFont = new StyleObject ();
		yAxisValuesFont.name = "_SdYAxisValuesFont";
		yAxisValuesFont.align = "right";
		yAxisValuesFont.valign = "middle";
		yAxisValuesFont.font = this.params.outCnvBaseFont;
		yAxisValuesFont.size = this.params.outCnvBaseFontSize;
		yAxisValuesFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.YAXISVALUES, yAxisValuesFont, this.styleM.TYPE.FONT, null);
		delete yAxisValuesFont;
		//-----------------------------------------------------------------//
		//Default font object for DataLabels
		//-----------------------------------------------------------------//
		var dataLabelsFont = new StyleObject ();
		dataLabelsFont.name = "_SdDataLabelsFont";
		dataLabelsFont.align = "center";
		dataLabelsFont.valign = "bottom";
		dataLabelsFont.font = this.params.outCnvBaseFont;
		dataLabelsFont.size = this.params.outCnvBaseFontSize;
		dataLabelsFont.color = this.params.outCnvBaseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.DATALABELS, dataLabelsFont, this.styleM.TYPE.FONT, null);
		delete dataLabelsFont;
		//-----------------------------------------------------------------//
		//Default font object for DataValues
		//-----------------------------------------------------------------//
		var dataValuesFont = new StyleObject ();
		dataValuesFont.name = "_SdDataValuesFont";
		dataValuesFont.align = "center";
		dataValuesFont.valign = "middle";
		dataValuesFont.font = this.params.baseFont;
		dataValuesFont.size = this.params.baseFontSize;
		dataValuesFont.color = this.params.baseFontColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.DATAVALUES, dataValuesFont, this.styleM.TYPE.FONT, null);
		delete dataValuesFont;
		//-----------------------------------------------------------------//
		//Default font object for ToolTip
		//-----------------------------------------------------------------//
		var toolTipFont = new StyleObject ();
		toolTipFont.name = "_SdToolTipFont";
		toolTipFont.font = this.params.baseFont;
		toolTipFont.size = this.params.baseFontSize;
		toolTipFont.color = this.params.baseFontColor;
		toolTipFont.bgcolor = this.params.toolTipBgColor;
		toolTipFont.bordercolor = this.params.toolTipBorderColor;
		//Over-ride
		this.styleM.overrideStyle (this.objects.TOOLTIP, toolTipFont, this.styleM.TYPE.FONT, null);
		delete toolTipFont;
		//-----------------------------------------------------------------//
		//Default Effect (Shadow) object for DataPlot
		//-----------------------------------------------------------------//
		if (this.params.showBarShadow)
		{
			var dataPlotShadow = new StyleObject ();
			dataPlotShadow.name = "_SdDataPlotShadow";
			//Over-ride
			this.styleM.overrideStyle (this.objects.DATAPLOT, dataPlotShadow, this.styleM.TYPE.SHADOW, null);
			delete dataPlotShadow;
		}
		//-----------------------------------------------------------------//
		//Default Animation object for DataPlot (if required)
		//-----------------------------------------------------------------//
		if (this.params.defaultAnimation)
		{
			var dataPlotAnim = new StyleObject ();
			dataPlotAnim.name = "_SdDataPlotAnim";
			dataPlotAnim.param = "_xscale";
			dataPlotAnim.easing = "regular";
			dataPlotAnim.wait = 0;
			dataPlotAnim.start = 0;
			dataPlotAnim.duration = 1;
			//Over-ride
			this.styleM.overrideStyle (this.objects.DATAPLOT, dataPlotAnim, this.styleM.TYPE.ANIMATION, "_xscale");
			delete dataPlotAnim;
		}
		//-----------------------------------------------------------------//
		
	}
	/**
	* calcVLinesPos method calculates the x position for the various
	* vLines defined. Also, it validates them.
	*/
	private function calcVLinesPos ()
	{
		var i : Number;
		//Iterate through all the vLines
		for (i = 1; i <= numVLines; i ++)
		{
			//If the vLine is after 1st data and before last data
			if (this.vLines [i].index > 0 && this.vLines [i].index < this.num)
			{
				//Set it's y position
				this.vLines [i].y = this.data [this.vLines [i].index].y + (this.data [this.vLines [i].index + 1].y - this.data [this.vLines [i].index].y) / 2;
			} else 
			{
				//Invalidate it
				this.vLines [i].isValid = false;
			}
		}
	}
	/**
	* calculatePoints method calculates the various points on the chart.
	*/
	private function calculatePoints ()
	{
		//Loop variable
		var i : Number;
		//Format all the numbers on the chart and store their display values
		//We format and store here itself, so that later, whenever needed,
		//we just access displayValue instead of formatting once again.
		//Also set tool tip text values
		var toolText : String
		for (i = 1; i <= this.num; i ++)
		{
			//Format and store
			this.data [i].displayValue = formatNumber (this.data [i].value, this.params.formatNumber, this.params.decimals, this.params.forceDecimals, this.params.formatNumberScale, this.params.defaultNumberScale, this.config.nsv, this.config.nsu, this.params.numberPrefix, this.params.numberSuffix);
			//Tool tip text
			//If labels have been defined
			toolText = (this.data [i].label != "") ? (this.data [i].label + this.params.toolTipSepChar) : "";
			toolText = toolText + this.data [i].displayValue;
			this.data [i].toolText = getFV (this.data [i].toolText, toolText);
		}
		//Based on label step, set showLabel of each data point as required.
		//Visible label count
		var visibleCount : Number = 0;
		var finalVisibleCount : Number = 0;
		for (i = 1; i <= this.num; i ++)
		{
			//Now, the label can be preset to be hidden (set via XML)
			if (this.data [i].showLabel)
			{
				visibleCount ++;
				//If label step is defined, we need to set showLabel of those
				//labels which fall on step as false.
				if ((i - 1) % this.params.labelStep == 0)
				{
					this.data [i].showLabel = true;
				} else 
				{
					this.data [i].showLabel = false;
				}
			}
			//Update counter
			finalVisibleCount = (this.data [i].showLabel) ? (finalVisibleCount + 1) : (finalVisibleCount);
		}
		//We now need to calculate the available Height on the canvas.
		//Available Height = total Chart Height minus the list below
		// - Top and Bottom Margin
		// - Caption and Sub Caption (including padding)
		// - yAxisName (if to be shown)
		// - yAxisValues
		// - Trend line display values.
		//Initialize canvasHeight to total height minus margins
		var canvasHeight : Number = this.height - (this.params.chartTopMargin + this.params.chartBottomMargin);
		//Set canvasStartY
		var canvasStartY : Number = this.params.chartTopMargin;
		//Now, if we've to show caption
		if (this.params.caption != "")
		{
			//Create text field to get height
			var captionObj : Object = createText (true, this.params.caption, this.tfTestMC, 1, testTFX, testTFY, 0, this.styleM.getTextStyle (this.objects.CAPTION) , false, 0, 0);
			//Store the height
			canvasStartY = canvasStartY + captionObj.height;
			canvasHeight = canvasHeight - captionObj.height;
			//Create element for caption - to store width & height
			this.elements.caption = returnDataAsElement (0, 0, captionObj.width, captionObj.height);
			delete captionObj;
		}
		//Now, if we've to show sub-caption
		if (this.params.subCaption != "")
		{
			//Create text field to get height
			var subCaptionObj : Object = createText (true, this.params.subCaption, this.tfTestMC, 1, testTFX, testTFY, 0, this.styleM.getTextStyle (this.objects.SUBCAPTION) , false, 0, 0);
			//Store the height
			canvasStartY = canvasStartY + subCaptionObj.height;
			canvasHeight = canvasHeight - subCaptionObj.height;
			//Create element for sub caption - to store height
			this.elements.subCaption = returnDataAsElement (0, 0, subCaptionObj.width, subCaptionObj.height);
			delete subCaptionObj;
		}
		//Now, if either caption or sub-caption was shown, we also need to adjust caption padding
		if (this.params.caption != "" || this.params.subCaption != "")
		{
			//Account for padding
			canvasStartY = canvasStartY + this.params.captionPadding;
			canvasHeight = canvasHeight - this.params.captionPadding;
		}
		//We now finally have canvas start Y and canvas height
		//Accomodate width for y-axis values. Now, y-axis values conists of two parts
		//(for backward compatibility) - limits (upper and lower) and div line values.
		//So, we'll have to individually run through both of them.
		var yAxisValMaxHeight : Number = 0;
		var divLineObj : Object;
		var divStyle : Object = this.styleM.getTextStyle (this.objects.YAXISVALUES);
		this.config.yValAreaHeight = 0;
		//Iterate through all the div line values
		for (i = 1; i < this.divLines.length; i ++)
		{
			//If div line value is to be shown
			if (this.divLines [i].showValue)
			{
				//If it's the first or last div Line (limits), and it's to be shown
				if ((i == 1) || (i == this.divLines.length - 1))
				{
					if (this.params.showLimits)
					{
						//Get the width of the text
						divLineObj = createText (true, this.divLines [i].displayValue, this.tfTestMC, 1, testTFX, testTFY, 0, divStyle, false, 0, 0);
						//Accomodate
						yAxisValMaxHeight = (divLineObj.height > yAxisValMaxHeight) ? (divLineObj.height) : (yAxisValMaxHeight);
					}
				} else 
				{
					//It's a div interval - div line
					//So, check if we've to show div line values
					if (this.params.showDivLineValues)
					{
						//Get the width of the text
						divLineObj = createText (true, this.divLines [i].displayValue, this.tfTestMC, 1, testTFX, testTFY, 0, divStyle, false, 0, 0);
						//Accomodate
						yAxisValMaxHeight = (divLineObj.height > yAxisValMaxHeight) ? (divLineObj.height) : (yAxisValMaxHeight);
					}
				}
			}
		}
		delete divLineObj;
		if (yAxisValMaxHeight > 0)
		{
			canvasHeight = canvasHeight - yAxisValMaxHeight - this.params.yAxisValuesPadding;
			//Store the y-axis values total height. We'll need it later when positioning trend values.
			this.config.yValAreaHeight = yAxisValMaxHeight + this.params.yAxisValuesPadding;
		}
		//Also iterate through all trend lines whose values are to be shown
		//Get style object
		var trendStyle : Object = this.styleM.getTextStyle (this.objects.TRENDVALUES);
		var trendObj : Object;
		var maxTrendHeight : Number = 0;
		for (i = 1; i <= this.numTrendLines; i ++)
		{
			if (this.trendLines [i].isValid)
			{
				//If it's a valid trend line get the height of the text
				trendObj = createText (true, this.trendLines [i].displayValue, this.tfTestMC, 1, testTFX, testTFY, 0, trendStyle, false, 0, 0);
				//Accomodate
				maxTrendHeight = (trendObj.height > maxTrendHeight) ? (trendObj.height) : (maxTrendHeight);
			}
		}
		if (maxTrendHeight > 0)
		{
			canvasHeight = canvasHeight - maxTrendHeight - this.params.yAxisValuesPadding;
		}
		delete trendObj;
		//Accomodate space for yAxisName (if to be shown);
		if (this.params.yAxisName != "")
		{
			//Create text field to get height
			var yAxisNameObj : Object = createText (true, this.params.yAxisName, this.tfTestMC, 1, testTFX, testTFY, 0, this.styleM.getTextStyle (this.objects.YAXISNAME) , false, 0, 0);
			//Store the height
			canvasHeight = canvasHeight - yAxisNameObj.height - this.params.yAxisNamePadding;
			//Object to store width and height of xAxisName
			this.elements.yAxisName = returnDataAsElement (0, 0, yAxisNameObj.width, yAxisNameObj.height);
			delete yAxisNameObj;
		}
		//We finally have canvas height and start Y Position
		//Round them off to avoid distorted pixels
		canvasStartY = int (canvasStartY);
		canvasHeight = int (canvasHeight);
		//-----------------------------------------------------------------------------------//
		//Now, we need to calculate the available Width on the canvas.
		//Available Width = total Chart width minus the list below
		// - Left and Right Margins
		// - xAxis Name
		// - Width of data labels
		var canvasWidth : Number = this.width - (this.params.chartLeftMargin + this.params.chartRightMargin);
		//Set canvas startX
		var canvasStartX : Number = this.params.chartLeftMargin;
		//Now, if x-axis name is to be shown, simulate it and get the width
		if (this.params.xAxisName != "")
		{
			//Get style object
			var xAxisNameStyle : Object = this.styleM.getTextStyle (this.objects.XAXISNAME);
			if (this.params.rotateXAxisName)
			{
				//Set embedded font - as we need to rotate
				xAxisNameStyle.font = _embeddedFont;
				//Un-bold and un-italicize
				xAxisNameStyle.bold = false;
				xAxisNameStyle.italic = false;
				//Create text field to get width
				var xAxisNameObj : Object = createText (true, this.params.xAxisName, this.tfTestMC, 1, testTFX, testTFY, 90, xAxisNameStyle, false, 0, 0);
				//Accomodate width and padding
				canvasStartX = canvasStartX + xAxisNameObj.width + this.params.xAxisNamePadding;
				canvasWidth = canvasWidth - xAxisNameObj.width - this.params.xAxisNamePadding;
				//Create element for xAxisName - to store width/height
				this.elements.xAxisName = returnDataAsElement (0, 0, xAxisNameObj.width, xAxisNameObj.height);
			}else
			{
				//If the x-axis name is not to be rotated
				//Calculate the width of the text if in full horizontal mode
				//Create text field to get width
				var xAxisNameObj : Object = createText (true, this.params.xAxisName, this.tfTestMC, 1, testTFX, testTFY, 0, xAxisNameStyle, false, 0, 0);
				//Get a value for this.params.xAxisNameWidth
				this.params.xAxisNameWidth = Number (getFV (this.params.xAxisNameWidth, xAxisNameObj.width));
				//Get the lesser of the width (to avoid un-necessary space)
				this.params.xAxisNameWidth = Math.min (this.params.xAxisNameWidth, xAxisNameObj.width);
				//Accomodate width and padding
				canvasStartX = canvasStartX + this.params.xAxisNameWidth + this.params.xAxisNamePadding;
				canvasWidth = canvasWidth - this.params.xAxisNameWidth - this.params.xAxisNamePadding;
				//Create element for xAxisName - to store width/height
				this.elements.xAxisName = returnDataAsElement (0, 0, this.params.xAxisNameWidth, xAxisNameObj.height);
			}
			delete xAxisNameStyle;
			delete xAxisNameObj;
		}
		//Now, if data labels are to be shown, we need to account for their widths
		//Placeholder to store max width
		var maxLabelWidth : Number = 0;
		this.config.labelAreaWidth = 0;
		this.config.wrapLabels = false;
		this.config.labelAreaHeight = 0;
		var labelObj : Object;
		var labelStyleObj : Object = this.styleM.getTextStyle (this.objects.DATALABELS);
		//We iterate through all the labels, and check width
		var strLabels : Array = new Array ();
		//Iterate through all the labels and check width
		for (i = 1; i <= this.num; i ++)
		{
			//If the label is to be shown
			if (this.data [i].showLabel)
			{
				//Create text box and get height
				labelObj = createText (true, this.data [i].label, this.tfTestMC, 1, testTFX, testTFY, 0, labelStyleObj, false, 0, 0);
				//Store the larger
				maxLabelWidth = (labelObj.width > maxLabelWidth) ? (labelObj.width) : (maxLabelWidth);
				//Store height of label - we need height to wrap labels later (if required).
				this.config.labelAreaHeight = (labelObj.height > this.config.labelAreaHeight) ?labelObj.height : this.config.labelAreaHeight;
			}
		}
		//Width of label cannot be greater than 60% of currently available width
		//Otherwise, the chart won't show up properly
		if (maxLabelWidth > (canvasWidth * 0.6))
		{
			maxLabelWidth = canvasWidth * 0.6;
			//Set wrap labels flag to on.
			this.config.wrapLabels = true;
		}
		if (maxLabelWidth > 0)
		{
			this.config.labelAreaWidth = maxLabelWidth;
			//Deduct the calculated label height from canvas height
			canvasStartX = canvasStartX + this.config.labelAreaWidth + this.params.labelPadding;
			canvasWidth = canvasWidth - this.config.labelAreaWidth - this.params.labelPadding;
		}
		//Delete objects
		delete labelObj;
		delete labelStyleObj;
		//We finally have canvas Width and canvas Start X
		//Round them off finally to avoid distorted pixels
		canvasStartX = int (canvasStartX);
		canvasWidth = int (canvasWidth);
		//Create an element to represent the canvas now.
		this.elements.canvas = returnDataAsElement (canvasStartX, canvasStartY, canvasWidth, canvasHeight);
		//We now need to calculate the position of columns on the chart.
		//Base Plane position - Base plane is the y-plane from which columns start.
		//If there's a 0 value in between yMin,yMax, base plane represents 0 value.
		//Else, it's yMin
		if (this.config.yMax >= 0 && this.config.yMin < 0)
		{
			//Negative number present - so set basePlanePos as 0 value
			this.config.basePlanePos = this.getAxisPosition (0, this.config.yMax, this.config.yMin, this.elements.canvas.x, this.elements.canvas.toX, false, 0);
		} else 
		{
			//No negative numbers - so set basePlanePos as yMin value
			this.config.basePlanePos = this.getAxisPosition (this.config.yMin, this.config.yMax, this.config.yMin, this.elements.canvas.x, this.elements.canvas.toX, false, 0);
		}
		//Now, calculate the spacing on canvas and individual column width
		var plotSpace : Number = (this.params.plotSpacePercent / 100) * this.elements.canvas.h;
		//Individual bar height.
		var barHeight : Number = (this.elements.canvas.h - plotSpace) / this.num;
		//Max bar height can be 50 - so if exceeded, reset width and space
		//Now, there can be an exception if this.params.plotSpacePercent has
		//been defined
		if (barHeight > 50 && (this.params.plotSpacePercent == 20))
		{
			barHeight = 50;
			plotSpace = this.elements.canvas.h - (this.num * barHeight);
		}
		//We finally have total plot space and bar height
		//Store it in config
		this.config.plotSpace = plotSpace;
		this.config.barHeight = barHeight;
		//Get space between two bars
		var interBarSpace : Number = plotSpace / (this.num + 1);
		//Store in config.
		this.config.interBarSpace = interBarSpace;
		var dataEndX : Number;
		//Now, store the positions of the bars
		for (i = 1; i <= this.num; i ++)
		{
			//Set the x position
			this.data [i].x = this.config.basePlanePos;
			//Y Position
			this.data [i].y = this.elements.canvas.y + (interBarSpace * i) + barHeight * (i - 0.5);
			//Width for each column
			dataEndX = this.getAxisPosition (this.data [i].value, this.config.yMax, this.config.yMin, this.elements.canvas.x, this.elements.canvas.toX, false, 0);
			//Get the width.
			this.data [i].w = (dataEndX - this.config.basePlanePos);
			//Width
			this.data [i].h = barHeight;
			//Store value textbox X position
			this.data [i].valTBX = dataEndX;
		}
	}
	/**
	* allotDepths method allots the depths for various chart objects
	* to be rendered. We do this before hand, so that we can later just
	* go on rendering chart objects, without swapping.
	*/
	private function allotDepths () : Void
	{
		//Background
		this.dm.reserveDepths ("BACKGROUND", 1);
		//Click URL Handler
		this.dm.reserveDepths ("CLICKURLHANDLER", 1);
		//Background SWF
		this.dm.reserveDepths ("BGSWF", 1);
		//Canvas
		this.dm.reserveDepths ("CANVAS", 1);
		//If vertical grid is to be shown
		if (this.params.showAlternateVGridColor)
		{
			this.dm.reserveDepths ("VGRID", Math.ceil ((this.divLines.length + 1) / 2));
		}
		//Div Lines and their labels
		this.dm.reserveDepths ("DIVLINES", (this.divLines.length * 2));
		//Caption
		this.dm.reserveDepths ("CAPTION", 1);
		//Sub-caption
		this.dm.reserveDepths ("SUBCAPTION", 1);
		//X-Axis Name
		this.dm.reserveDepths ("XAXISNAME", 1);
		//Y-Axis Name
		this.dm.reserveDepths ("YAXISNAME", 1);
		//Trend lines below plot (lines and their labels)
		this.dm.reserveDepths ("TRENDLINESBELOW", this.numTrendLinesBelow);
		this.dm.reserveDepths ("TRENDVALUESBELOW", this.numTrendLinesBelow);
		//Vertical div lines
		this.dm.reserveDepths ("VLINES", this.numVLines);
		//Data Labels
		this.dm.reserveDepths ("DATALABELS", this.num);
		//Data Columns
		this.dm.reserveDepths ("DATAPLOT", this.num);
		//Zero Plane
		this.dm.reserveDepths ("ZEROPLANE", 2);
		//Trend lines below plot (lines and their labels)
		this.dm.reserveDepths ("TRENDLINESABOVE", (this.numTrendLines - this.numTrendLinesBelow));
		this.dm.reserveDepths ("TRENDVALUESABOVE", (this.numTrendLines - this.numTrendLinesBelow));
		//Canvas Border
		this.dm.reserveDepths ("CANVASBORDER", 1);
		//Data Values
		this.dm.reserveDepths ("DATAVALUES", this.num);
	}
	//--------------- VISUAL RENDERING METHODS -------------------------//
	/**
	* drawHeaders method renders the following on the chart:
	* CAPTION, SUBCAPTION, XAXISNAME, YAXISNAME
	*/
	private function drawHeaders ()
	{
		//Render caption
		if (this.params.caption != "")
		{
			var captionStyleObj : Object = this.styleM.getTextStyle (this.objects.CAPTION);
			captionStyleObj.align = "center";
			captionStyleObj.vAlign = "bottom";
			var captionObj : Object = createText (false, this.params.caption, this.cMC, this.dm.getDepth ("CAPTION") , this.elements.canvas.x + (this.elements.canvas.w / 2) , this.params.chartTopMargin, 0, captionStyleObj, false, 0, 0);
			//Apply animation
			if (this.params.animation)
			{
				this.styleM.applyAnimation (captionObj.tf, this.objects.CAPTION, this.macro, this.elements.canvas.x + (this.elements.canvas.w / 2) - (this.elements.caption.w / 2) , 0, this.params.chartTopMargin, 0, 100, null, null, null);
			}
			//Apply filters
			this.styleM.applyFilters (captionObj.tf, this.objects.CAPTION);
			//Delete
			delete captionObj;
			delete captionStyleObj;
		}
		//Render sub caption
		if (this.params.subCaption != "")
		{
			var subCaptionStyleObj : Object = this.styleM.getTextStyle (this.objects.SUBCAPTION);
			subCaptionStyleObj.align = "center";
			subCaptionStyleObj.vAlign = "top";
			var subCaptionObj : Object = createText (false, this.params.subCaption, this.cMC, this.dm.getDepth ("SUBCAPTION") , this.elements.canvas.x + (this.elements.canvas.w / 2) , this.elements.canvas.y - this.params.captionPadding, 0, subCaptionStyleObj, false, 0, 0);
			//Apply animation
			if (this.params.animation)
			{
				this.styleM.applyAnimation (subCaptionObj.tf, this.objects.SUBCAPTION, this.macro, this.elements.canvas.x + (this.elements.canvas.w / 2) - (this.elements.subCaption.w / 2) , 0, this.elements.canvas.y - this.params.captionPadding - this.elements.subCaption.h, 0, 100, null, null, null);
			}
			//Apply filters
			this.styleM.applyFilters (subCaptionObj.tf, this.objects.SUBCAPTION);
			//Delete
			delete subCaptionObj;
			delete subCaptionStyleObj;
		}
		//Render y-axis name
		if (this.params.yAxisName != "")
		{
			var yAxisNameStyleObj : Object = this.styleM.getTextStyle (this.objects.YAXISNAME);
			yAxisNameStyleObj.align = "center";
			yAxisNameStyleObj.vAlign = "top";
			var yAxisNameObj : Object = createText (false, this.params.yAxisName, this.cMC, this.dm.getDepth ("YAXISNAME") , this.elements.canvas.x + (this.elements.canvas.w / 2) , this.height - this.params.chartBottomMargin, 0, yAxisNameStyleObj, false, 0, 0);
			//Apply animation
			if (this.params.animation)
			{
				this.styleM.applyAnimation (yAxisNameObj.tf, this.objects.YAXISNAME, this.macro, this.elements.canvas.x + (this.elements.canvas.w / 2) , 0, yAxisNameObj.tf._y, 0, 100, null, null, null);
			}
			//Apply filters
			this.styleM.applyFilters (yAxisNameObj.tf, this.objects.YAXISNAME);
			//Delete
			delete yAxisNameObj;
			delete yAxisNameStyleObj;
		}
		//Render x-axis name
		if (this.params.xAxisName != "")
		{
			var xAxisNameStyleObj : Object = this.styleM.getTextStyle (this.objects.XAXISNAME);
			//Set alignment parameters
			xAxisNameStyleObj.align = "left";
			xAxisNameStyleObj.vAlign = "middle";
			//If the name is to be rotated
			if (this.params.rotateXAxisName)
			{
				//Set font as the embedded font
				xAxisNameStyleObj.font = _embeddedFont;
				//Set bold/italic to false.
				xAxisNameStyleObj.bold = false;
				xAxisNameStyleObj.italic = false;
				var xAxisNameObj : Object = createText (false, this.params.xAxisName, this.cMC, this.dm.getDepth ("XAXISNAME") , this.params.chartLeftMargin, this.elements.canvas.y + (this.elements.canvas.h / 2) , 270, xAxisNameStyleObj, false, 0, 0);
				//Apply animation
				if (this.params.animation)
				{
					this.styleM.applyAnimation (xAxisNameObj.tf, this.objects.XAXISNAME, this.macro, this.params.chartLeftMargin, 0, this.elements.canvas.y + (this.elements.canvas.h / 2) + (this.elements.xAxisName.h / 2) , 0, 100, null, null, null);
				}
			}else
			{
				//We show horizontal name
				//Adding 1 to this.params.xAxisNameWidth and then passing to avoid line breaks
				var xAxisNameObj : Object = createText (false, this.params.xAxisName, this.cMC, this.dm.getDepth ("XAXISNAME") , this.params.chartLeftMargin, this.elements.canvas.y + (this.elements.canvas.h / 2) , 0, xAxisNameStyleObj, true, this.params.xAxisNameWidth + 1, this.elements.canvas.h);
				//Apply animation
				if (this.params.animation)
				{
					this.styleM.applyAnimation (xAxisNameObj.tf, this.objects.XAXISNAME, this.macro, this.params.chartLeftMargin, 0, xAxisNameObj.tf._y, 0, 100, null, null, null);
				}
			}
			//Apply filters
			this.styleM.applyFilters (xAxisNameObj.tf, this.objects.XAXISNAME);
			//Delete
			delete xAxisNameObj;
			delete xAxisNameStyleObj;
		}
		//Clear Interval
		clearInterval (this.config.intervals.headers);
	}
	/**
	* drawLabels method draws the x-axis labels based on the oarameters.
	*/
	private function drawLabels ()
	{
		var labelObj : Object;
		var labelStyleObj : Object = this.styleM.getTextStyle (this.objects.DATALABELS);
		var labelYShift : Number;
		var staggerCycle : Number = 0;
		var staggerAddFn : Number = 1;
		var depth : Number = this.dm.getDepth ("DATALABELS");
		var i : Number;
		for (i = 1; i <= this.num; i ++)
		{
			//If the label is to be shown
			if (this.data [i].showLabel)
			{
				//Render normal label
				labelStyleObj.align = "right";
				labelStyleObj.vAlign = "middle";
				labelObj = createText (false, this.data [i].label, this.cMC, depth, this.elements.canvas.x - this.params.labelPadding, this.data [i].y, 0, labelStyleObj, this.config.wrapLabels, this.config.labelAreaWidth, this.config.labelAreaHeight);
				//Apply filter
				this.styleM.applyFilters (labelObj.tf, this.objects.DATALABELS);
				//Apply animation
				if (this.params.animation)
				{
					this.styleM.applyAnimation (labelObj.tf, this.objects.DATALABELS, this.macro, labelObj.tf._x, 0, labelObj.tf._y, 0, 100, null, null, null);
				}
				//Increase depth
				depth ++;
			}
		}
		//Clear interval
		clearInterval (this.config.intervals.labels);
	}
	/**
	* drawBars method draws the bars on the chart
	*/
	private function drawBars ()
	{
		//Variables
		var barMC : MovieClip;
		var barIns : Bar2D;
		var depth : Number = this.dm.getDepth ("DATAPLOT");
		var i : Number;
		//Create function storage containers for Delegate functions
		var fnRollOver : Function, fnClick : Function;
		//Iterate through all columns
		for (i = 1; i <= this.num; i ++)
		{
			//If defined
			if (this.data [i].isDefined)
			{
				//Create an empty movie clip for this bar
				barMC = this.cMC.createEmptyMovieClip ("Bar_" + i, depth);
				//Create bar instance
				barIns = new Bar2D (barMC, this.data [i].w, this.data [i].h, this.params.plotBorderColor, this.params.plotBorderAlpha, this.params.plotBorderThickness, this.data [i].color, this.data [i].alpha, this.data [i].ratio, this.params.plotFillAngle, false, this.data [i].dashed, this.params.plotBorderDashLen, this.params.plotBorderDashGap);
				//Draw the bar
				barIns.draw ();
				//Set x and y position
				barMC._x = this.data [i].x;
				barMC._y = this.data [i].y;
				//Set the alpha of entire bar as the first alpha in list of alpha
				barMC._alpha = this.data [i].alpha [0];
				//Apply animation
				if (this.params.animation)
				{
					this.styleM.applyAnimation (barMC, this.objects.DATAPLOT, this.macro, this.data [i].x, 0, this.data [i].y, 0, 100, 100, 100, null);
				}
				//Apply filters
				this.styleM.applyFilters (barMC, this.objects.DATAPLOT);
				//Event handlers for tool tip
				if (this.params.showToolTip)
				{
					//Create Delegate for roll over function barOnRollOver
					fnRollOver = Delegate.create (this, barOnRollOver);
					//Set the index
					fnRollOver.index = i;
					//Assing the delegates to movie clip handler
					barMC.onRollOver = fnRollOver;
					//Set roll out and mouse move too.
					barMC.onRollOut = barMC.onReleaseOutside = Delegate.create (this, barOnRollOut);
					barMC.onMouseMove = Delegate.create (this, barOnMouseMove);
				}
				//Click handler for links - only if link for this column has been defined and click URL
				//has not been defined.
				if (this.data [i].link != "" && this.data [i].link != undefined && this.params.clickURL == "")
				{
					//Create delegate function
					fnClick = Delegate.create (this, barOnClick);
					//Set index
					fnClick.index = i;
					//Assign
					barMC.onRelease = fnClick;
				}
				else
				{
					//Do not use hand cursor
					barMC.useHandCursor = (this.params.clickURL == "") ?false : true;
				}
				//Increase depth
				depth ++;
			}
		}
		//Clear interval
		clearInterval (this.config.intervals.plot);
	}
	/**
	* drawValues method draws the values on the chart.
	*/
	private function drawValues () : Void
	{
		//Get value text style
		var valueStyleObj : Object = this.styleM.getTextStyle (this.objects.DATAVALUES);
		var angle : Number = 0;
		//Container object
		var valueObj : MovieClip;
		//Depth
		var depth : Number = this.dm.getDepth ("DATAVALUES");
		//Loop var
		var i : Number;
		var xPos : Number;
		var align : String, vAlign : String;
		////Iterate through all columns
		for (i = 1; i <= this.num; i ++)
		{
			//If defined and value is to be shown
			if (this.data [i].isDefined && this.data [i].showValue)
			{
				//Get the x position based on placeValuesInside and column height
				if (this.params.placeValuesInside)
				{
					//Vertical alignment position of the value box
					align = (this.data [i].w >= 0) ?"right" : "left";
					xPos = (this.data [i].w >= 0) ? (this.data [i].valTBX - this.params.valuePadding) : (this.data [i].valTBX + this.params.valuePadding);
				}else
				{
					align = (this.data [i].w >= 0) ?"left" : "right";
					xPos = (this.data [i].w >= 0) ? (this.data [i].valTBX + this.params.valuePadding) : (this.data [i].valTBX - this.params.valuePadding);
				}
				//v-Align position
				vAlign = "middle";
				//Convey alignment to rendering object
				valueStyleObj.align = align;
				valueStyleObj.vAlign = vAlign;
				angle = 0;
				valueObj = createText (false, this.data [i].displayValue, this.cMC, depth, xPos, this.data [i].y, angle, valueStyleObj, false, 0, 0);
				//Now, we need to adjust those labels which are falling out of Width.
				if (this.params.placeValuesInside && ((this.params.valuePadding + valueObj.width) >= Math.abs (this.data [i].w)))
				{
					//If the values are to be placed inside bars, but the width of the bars
					//is less than the width of textbox (+ padding), we show the textbox left/right of bar
					//For positive column
					if (this.data [i].w >= 0)
					{
						//Show on right
						valueObj.tf._x = this.data [i].x + this.data [i].w + this.params.valuePadding;
					}else
					{
						//Negative Column - show on left
						valueObj.tf._x = this.data [i].x - Math.abs (this.data [i].w) - this.params.valuePadding - valueObj.width;
					}
				}
				//Next, we adjust those labels are falling out of right canvas area
				//For Positive bars only
				if (( ! this.params.placeValuesInside) && (this.data [i].w >= 0) && ((xPos + valueObj.width) >= this.elements.canvas.toX))
				{
					//Data value is colliding with the right side of canvas. So we need to place it within
					//the bar
					valueObj.tf._x = xPos - (2 * this.params.valuePadding) - valueObj.width;
				}
				//Now, we adjust those labels are falling out of left canvas area
				//For Negative Columns Only
				if (( ! this.params.placeValuesInside) && (this.data [i].w < 0) && ((xPos - valueObj.width) <= this.elements.canvas.x))
				{
					//Data value is colliding with the left side of canvas. So we need to place it within
					//the bar
					valueObj.tf._x = valueObj.tf._x + (2 * this.params.valuePadding) + valueObj.width;
				}
				//Apply filter
				this.styleM.applyFilters (valueObj.tf, this.objects.DATAVALUES);
				//Apply animation
				if (this.params.animation)
				{
					this.styleM.applyAnimation (valueObj.tf, this.objects.DATAVALUES, this.macro, valueObj.tf._x, 0, valueObj.tf._y, 0, 100, null, null, null);
				}
				//Increase depth
				depth ++;
			}
		}
		//Clear interval
		clearInterval (this.config.intervals.dataValues);
	}
	/**
	* setContextMenu method sets the context menu for the chart.
	* For this chart, the context items are "Print Chart".
	*/
	private function setContextMenu () : Void
	{
		var chartMenu : ContextMenu = new ContextMenu ();
		chartMenu.hideBuiltInItems ();
		//Create a print chart contenxt menu item
		var printCMI : ContextMenuItem = new ContextMenuItem ("Print Chart", Delegate.create (this, printChart));
		//Push print item.
		chartMenu.customItems.push (printCMI);
		if (this.params.showFCMenuItem){
			//Push "About FusionCharts" Menu Item
			chartMenu.customItems.push(super.returnAbtMenuItem());		
		}
		//Assign the menu to cMC movie clip
		this.cMC.menu = chartMenu;
	}
	// -------------------- EVENT HANDLERS --------------------//
	/**
	* barOnRollOver is the delegat-ed event handler method that'll
	* be invoked when the user rolls his mouse over a bar.
	* This function is invoked, only if the tool tip is to be shown.
	* Here, we show the tool tip.
	*/
	private function barOnRollOver () : Void
	{
		//Index of bar is stored in arguments.caller.index
		var index : Number = arguments.caller.index;
		//Set tool tip text
		this.tTip.setText (this.data [index].toolText);
		//Show the tool tip
		this.tTip.show ();
	}
	/**
	* barOnRollOut method is invoked when the mouse rolls out
	* of bar. We just hide the tool tip here.
	*/
	private function barOnRollOut () : Void
	{
		//Hide the tool tip
		this.tTip.hide ();
	}
	/*
	* barOnMouseMove is called when the mouse position has changed
	* over bar. We reposition the tool tip.
	*/
	private function barOnMouseMove () : Void
	{
		//Reposition the tool tip only if it's in visible state
		if (this.tTip.visible ())
		{
			this.tTip.rePosition ();
		}
	}
	/**
	* barOnClick is invoked when the user clicks on a bar (if link
	* has been defined). We invoke the required link.
	*/
	private function barOnClick () : Void
	{
		//Index of bar is stored in arguments.caller.index
		var index : Number = arguments.caller.index;
		//Invoke the link
		super.invokeLink (this.data [index].link);
	}
	/**
	* reInit method re-initializes the chart. This method is basically called
	* when the user changes chart data through JavaScript. In that case, we need
	* to re-initialize the chart, set new XML data and again render.
	*/
	public function reInit () : Void
	{
		//Invoke super class's reInit
		super.reInit ();
		//Now initialize things that are pertinent to this class
		//but not defined in super class.
		//Re-set num to 0
		this.num = 0;
		//Re-create data array
		this.data = new Array ();
		//Whether negative number present
		this.config.negativePresent = false;
	}
}
